/*
    Simulate i2c signal by gpio
    用gpio模拟实现i2c协议
*/
#include <stdio.h>
#include "gpio-i2c.h"
#define PRE_START 0
#define PRE_STOP 1
int ret = 0; // save the last key function return
// =======base function config: gpio function, delay function======


// gpio function
unsigned char InitFunc()
{
    ret = gpio_init();
    if(ret)
    {
        printf("GPIO init error! code:%d",ret);
    }
	return ret;
}
void ExitFunc()
{
	gpio_exit();
}

// ==============finish base config
// pre check  see detail at readme.md: i2c note
void precheck()
{
	IICSCL_sw();
	IICSDA_sw();
	IICSCL_w(0);
	IICSDA_w(0);
	IICDELAY2(20);
	IICSDA_w(1);
	IICDELAY2(20);
}
void i2c_start()
{
    // set sda and scl on output mode
	precheck();
    IICSDA_sw();
    IICSCL_sw();
    IICSDA_w(1); // set sda high
    IICSCL_w(1); // set scl high
    IICDELAY(); // wait
    // start condition
    IICSDA_w(0);
    IICDELAY();
}

void i2c_stop()
{
    /* stop condition
        when scl was high, sda from low to high 
    */
	// precheck();
	IICSDA_sw();
	IICSCL_w(0);
	IICDELAY2(500);
	IICSDA_w(0);
	IICDELAY2(500);
	IICSCL_w(1);
	IICDELAY2(500);
	IICSDA_w(1);
}

/*
I2C receive ACK signal (use when write data)
return: 
	0 success
	other fail
*/
unsigned char i2c_read_ack()
{
	unsigned char r;
	IICSCL_w(0); // set scl low 
	IICSDA_sr();  // set sda on read mode 
	IICDELAY(); // wait 
	r = IICSDA_r(); // read sda value
	IICDELAY2(20);
	IICSDA_sw();
	IICSCL_w(1); // set scl high 
	IICDELAY(); // wait 
	return r;
}

/*
I2C send ACK signal	(used when read data)
*/
void i2c_send_ack()
{
	IICSDA_sw(); // set sda on write mode 
	IICSCL_w(0); // set scl low
	IICSDA_w(0); // set sda low, that means send the ACK signal 
	IICDELAY(); // wait 
	IICSCL_w(1); // set scl high
	IICDELAY();
	
}

/* write byte
return: 
	0 success
	other fail
*/
unsigned char i2c_write_byte(unsigned char b)
{
	int i;
	unsigned char r=0;
	IICSDA_sw();
	for (i=7; i>=0; i--)
	{
		IICSCL_w(0); // set scl low
		IICDELAY2(500);
		IICSDA_w(b & (1<<i)); // send bit from low to high 
		IICDELAY2(500);
		IICSCL_w(1); // set scl high finish a bit 
		IICDELAY();
	}
	r = i2c_read_ack();
	return r;
}

// read byte 
unsigned char i2c_read_byte()
{
	int i;
	unsigned char r = 0;
	IICSDA_sr(); // set sda on read mode 
	for (i=7; i>=0; i--)
	{
		IICSCL_w(0); // set scl low 
		IICDELAY();
		r = (r << 1) | IICSDA_r(); // read one bit 
		IICSCL_w(1); // set scl high 
		IICDELAY();
	}
	i2c_send_ack(); // send ACK signal to the slave 
	return r;
}

/*
	read data
		addr : device address
		buf : read buffer
		len : receive data length
*/
unsigned char i2c_read(unsigned char addr, unsigned char* buf, int len)
{
	int i;
	unsigned char r = 0;
	unsigned char t;
	i2c_start(); // start i2c 
	t = (addr << 1) | 1; // set the lowest bit high, it means read data
	r = i2c_write_byte(t);
	if(r != 0)
		return 1; // send byte error
	// read data
	for (i=0; i<len; i++)
	{
		buf[i] = i2c_read_byte();
	}
	i2c_stop(); // stop i2c
	return 0;
}

/*
	write data
		addr : device address
		buf : write buffer
		len : send data length
*/
unsigned char i2c_write (unsigned char addr, unsigned char* buf, int len)
{
	int i;
	unsigned char r = 0;
	unsigned char t;
	i2c_start(); // start i2c 
	t = (addr << 1) | 0; // set the lowest bit low, it means send data
	r = i2c_write_byte(t);
	for (i=0; i<len; i++)
	{
		r = r | i2c_write_byte(buf[i]);
	}
	i2c_stop();
	return r;
}

/* write data to device register*/
unsigned char i2c_writereg(unsigned deviceAddr,unsigned char regaddr, unsigned char value)
{
	unsigned char sdat[2];
	unsigned char r;
	sdat[0] = regaddr;
	sdat[1] = value;
	r = i2c_write(deviceAddr, sdat, 2);
	return r;
}

/*read data from device register*/
unsigned char i2c_readreg(unsigned deviceAddr,unsigned char regaddr, unsigned char *value)
{
	unsigned char r = 0;
	unsigned char t;
	i2c_start(); // start i2c 
	t = (deviceAddr << 1) | 0; // set the lowest bit low, it means send data
	r = i2c_write_byte(t); // device address and set write mode
	r = r | i2c_write_byte(regaddr); // send the register address
	i2c_start(); // restart
	t = (deviceAddr << 1) | 1; // it means get data
	r = r | i2c_write_byte(t); // device address and set read mode
		int i;
	IICSDA_sr(); // set sda on read mode 
	for (i=7; i>=0; i--)
	{
		IICSCL_w(0); // set scl low 
		IICDELAY();
		*value = (*value << 1) | IICSDA_r(); // read one bit 
		IICSCL_w(1); // set scl high 
		IICDELAY();
	}

	// IICSDA_sw(); // set sda on write mode 
	IICSCL_w(0); // set scl low
	// IICSDA_w(0); no ack
	IICDELAY(); // wait 
	IICSCL_w(1); // set scl high
	IICDELAY();
	IICSCL_w(0);
	IICSDA_sw();
	IICDELAY2(500);
	IICSDA_w(0);
	IICDELAY2(500);
	IICSCL_w(1);
	IICDELAY2(500);
	IICSDA_w(1);
	return r;
}